package com.lsj.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.lsj.config.redis.RedisKey;
import com.lsj.core.exception.CustomerException;
import com.lsj.core.lock.DistributedLock;
import com.lsj.core.lock.LockKey;
import com.lsj.core.response.ResponseType;
import com.lsj.core.util.ExceptionHelperUtil;
import com.lsj.core.util.QueryHelperUtil;
import com.lsj.dto.sys.*;
import com.lsj.mapper.SysOrgMapper;
import com.lsj.mapper.SysUserOrgMapper;
import com.lsj.mapper.entity.SysOrg;
import com.lsj.mapper.entity.SysUserOrg;
import com.lsj.service.OrgService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author:lin.sj
 * @Data:2021/7/14 11:59
 * @Desc:
 */
@Service
@Slf4j
public class OrgServiceImpl implements OrgService {
    @Autowired
    SysOrgMapper sysOrgMapper;
    @Autowired
    SysUserOrgMapper sysUserOrgMapper;

    /**
     * @Author: lin.sj
     * @Date: 2021/8/18 20:34
     * @Desc: 增加组织机构信息
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    @DistributedLock(value = LockKey.org_tree_key)
    public void addOrg(OrgAddDto dto) {
        //检查父级菜单是否存在
        SysOrg parentOrg = sysOrgMapper.selectById(dto.getOrgPid());
        if(parentOrg == null){
            throw new CustomerException(ResponseType.error,"父级菜单不存在，无法新增！");
        }else if(parentOrg.getIsLeaf()){
            //修改菜单为非叶子节点
            parentOrg.setIsLeaf(false);
            sysOrgMapper.updateById(parentOrg);
        }


        //获取当前level的菜单数量+1，即为新增菜单的sort
        int currentLevel = parentOrg.getLevel()+1;
        LambdaQueryWrapper<SysOrg> w = QueryHelperUtil.getLambdaWrapper(SysOrg.class);
        w.eq(SysOrg::getLevel,currentLevel).eq(SysOrg::getOrgPid,dto.getOrgPid());
        Integer sortCount = sysOrgMapper.selectCount(w)+1;

        //新增组织机构
        SysOrg sysOrg = Convert.convert(SysOrg.class, dto);
        sysOrg
                //设置默认的组织机构名称
                .setOrgName("新建组织机构")
                //设置到根节点为止的父节点集合信息
                .setOrgPids(getOrgPids(dto.getOrgPid()))
                //设置可用
                .setStatus(true)
                //设置排序
                .setSort(sortCount)
                //设置为叶子节点
                .setIsLeaf(true)
                //父节点level+1,即为当前节点的level
                .setLevel(currentLevel);
        sysOrgMapper.insert(sysOrg);
    }
    
    /**
     * @Author: lin.sj
     * @Date: 2021/8/18 20:34
     * @Desc: 删除组织机构，删除时要把所有缓存在redis的用户信息都清除掉
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames= RedisKey.USER_KEY,allEntries = true)
    @DistributedLock(value = LockKey.org_tree_key)
    public void deleteOrg(Long orgId) {
        if(orgId == 0){
            throw new CustomerException(ResponseType.error,"根节点不能删除");
        }
        //检查出该Id作为父节点的所有子节点
        LambdaQueryWrapper<SysOrg> w = QueryHelperUtil.getLambdaWrapper(SysOrg.class);
        w.like(SysOrg::getOrgPids,orgId);
        List<SysOrg> children = sysOrgMapper.selectList(w);


        //删除所有包含的用户与组织机构关系（子机构关系）,删除该组织机构
        LambdaQueryWrapper<SysUserOrg> userOrgWhere = QueryHelperUtil.getLambdaWrapper(SysUserOrg.class);
        for(SysOrg sysOrg:children){
            userOrgWhere.clear();
            userOrgWhere.eq(SysUserOrg::getOrgId,sysOrg.getId());
            sysUserOrgMapper.delete(userOrgWhere);
            sysOrgMapper.deleteById(sysOrg.getId());
        }


        //删除用户与组织的关系（本机构关系）
        userOrgWhere.clear();
        userOrgWhere.eq(SysUserOrg::getOrgId,orgId);
        sysUserOrgMapper.delete(userOrgWhere);

        //将排在后面的组织往前挪,9999足够大了，不会有那么多的菜单
        SysOrg sysOrg = sysOrgMapper.selectById(orgId);
        updateTreeSort(sysOrg.getSort(),9999,sysOrg.getLevel(),sysOrg.getOrgPid());

        //删除组织
        sysOrgMapper.deleteById(orgId);
        //判断父组织下是否还有节点，没有就更新父节点为叶子节点
        w.clear();
        w.eq(SysOrg::getOrgPid,sysOrg.getOrgPid());
        Integer childrenCount = sysOrgMapper.selectCount(w);
        if(childrenCount <=0){
            SysOrg preantOrg = sysOrgMapper.selectById(sysOrg.getOrgPid());
            preantOrg.setIsLeaf(false);
            sysOrgMapper.updateById(preantOrg);
        }
    }

    /**
     * @Author: lin.sj
     * @Date: 2021/8/18 20:35
     * @Desc: 更新组织机构信息，为了保持准确性，更新时要加入分布式锁
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = RedisKey.USER_KEY,allEntries = true)
    @DistributedLock(value = LockKey.org_tree_key)
    public void updateOrg(OrgUpdateDto dto) {
        //检查当前数据是否存在
        SysOrg sysOrg = sysOrgMapper.selectById(dto.getId());
        ExceptionHelperUtil.nullObjException(sysOrg,"当前数据不存在，无法更新！");

        //检查是否存在相同的组织机构名称或相同组织机构编码的菜单
        LambdaQueryWrapper<SysOrg> w = QueryHelperUtil.getLambdaWrapper(SysOrg.class);
        w
                .ne(SysOrg::getId,dto.getId())
                .and(wrapper ->
                        wrapper
                                .eq(SysOrg::getOrgName,dto.getOrgName())
                                .or()
                                .eq(SysOrg::getOrgCode,dto.getOrgCode()));
        Integer repeatCount = sysOrgMapper.selectCount(w);
        if(repeatCount > 0){
            throw new CustomerException(ResponseType.error,"存在相同组织名称或相同组织代码的组织结构，无法更新！");
        }

        //若当前组织机构状态不可用时，需要删除user_org表的关联关系,包括该组织机构下的所有子节点,且需要更新该组织机构下的子节点都不可用
        if(!dto.getStatus()){
            //将当前节点id加入要删除的role_menu关系集合
            List<Long> userOrgDeleteIds = new ArrayList<>();
            userOrgDeleteIds.add(dto.getId());
            //非叶子节点获取所有子孙节点的ID集合
            if(!sysOrg.getIsLeaf()){
                w.clear();
                w.like(SysOrg::getOrgPids,dto.getId());
                List<SysOrg> childrenOrgs = sysOrgMapper.selectList(w);
                //转换获取menuId
                List<Long> childIds = childrenOrgs.stream().map(SysOrg::getId).collect(Collectors.toList());
                userOrgDeleteIds.addAll(childIds);
                //设置子孙节点不可用
                childrenOrgs.stream().forEach(o -> {
                    o.setStatus(false);
                    sysOrgMapper.updateById(o);
                });
            }
            //删除关联关系
            LambdaQueryWrapper<SysUserOrg> userOrgWhere = QueryHelperUtil.getLambdaWrapper(SysUserOrg.class);
            userOrgWhere.in(SysUserOrg::getOrgId,userOrgDeleteIds);
            sysUserOrgMapper.delete(userOrgWhere);
        }

        //设置状态可用时，叶子需要检查上级菜单是否可用，否则无法设置可用
        if(dto.getStatus() && sysOrg.getIsLeaf()){
            w.clear();
            w.eq(SysOrg::getId,sysOrg.getOrgPid());
            SysOrg parentOrg = sysOrgMapper.selectOne(w);
            if(!parentOrg.getStatus()){
                throw new CustomerException(ResponseType.error,"当前【"+sysOrg.getOrgName()+"】的上级节点不可用，请先对上级节点设置可用！");
            }
        }

        //将更新的内容复制到数据库实体
        BeanUtil.copyProperties(dto,sysOrg);
        int updateCount = sysOrgMapper.updateById(sysOrg);
        if(updateCount <=0){
            throw new CustomerException(ResponseType.error,"更新失败，请重新尝试！");
        }
    }
    /**
     * @Author: lin.sj
     * @Date: 2021/8/18 20:35
     * @Desc: 更新组织结构排序信息
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    @DistributedLock(value = LockKey.org_tree_key)
    public void updateOrgSort(OrgSortDto orgSortDto) {
        if(orgSortDto.getOldSort() == 1 && orgSortDto.getOldSort() > orgSortDto.getNewSort()){
            throw new CustomerException(ResponseType.error,"菜单已经排在最前面了！");
        }
        SysOrg sysOrg = sysOrgMapper.selectById(orgSortDto.getId());

        LambdaQueryWrapper<SysOrg> w = QueryHelperUtil.getLambdaWrapper(SysOrg.class);
        w
                .eq(SysOrg::getOrgPid,sysOrg.getOrgPid());
        List<SysOrg> sysOrgs = sysOrgMapper.selectList(w);
        //判断当前是否向下排序且修改前的排序与当前层级的数量一致，此时不再允许想后排序
        if(orgSortDto.getOldSort() < orgSortDto.getNewSort() && orgSortDto.getOldSort() == sysOrgs.size()){
            throw new CustomerException(ResponseType.error,"菜单已经排在最后面了！");
        }

        //重新将组织机构进行排序
        updateTreeSort(orgSortDto.getOldSort(),orgSortDto.getNewSort(),orgSortDto.getLevel(),sysOrg.getOrgPid());


        sysOrg.setSort(orgSortDto.getNewSort());
        sysOrgMapper.updateById(sysOrg);
    }
    
    @Override
    public OrgDto getOrgById(Long orgId) {
        SysOrg sysOrg = sysOrgMapper.selectById(orgId);
        ExceptionHelperUtil.nullObjException(sysOrg,"当前组织结构信息不存在！");
        return Convert.convert(OrgDto.class,sysOrg);
    }

    /**
     * @Author: lin.sj
     * @Date: 2021/7/28 20:00
     * @Desc: 获取整个组织机构树
     **/
    @Override
    public List<OrgTreeDto> getPositiveOrgTree() {
        SysOrg parentOrg = sysOrgMapper.selectById(0);
        OrgTreeDto parentOrgTree = Convert.convert(OrgTreeDto.class, parentOrg);
        loadOrgTree(parentOrgTree);
        return Arrays.asList(parentOrgTree);
    }

    /**
     * @Author: lin.sj
     * @Date: 2021/7/27 20:56
     * @Desc: 递归查找菜单信息，形成树菜单
     **/
    private List<OrgTreeDto> loadOrgTree(OrgTreeDto tree){
        //根据父菜单Id查找子菜单
        LambdaQueryWrapper<SysOrg> w = QueryHelperUtil.getLambdaWrapper(SysOrg.class);
        w.eq(SysOrg::getOrgPid,tree.getId()).orderByAsc(SysOrg::getSort);
        List<SysOrg> sysOrgs = sysOrgMapper.selectList(w);

        List<OrgTreeDto> orgTreeDtos = new ArrayList<>();
        //循环子菜单
        for(SysOrg o:sysOrgs){
            OrgTreeDto org = Convert.convert(OrgTreeDto.class, o);
            //将当前的菜单加入到子菜单列表中
            orgTreeDtos.add(org);
            //如果不是叶子节点，就递归查找下一级菜单信息
            if(!o.getIsLeaf()){
                List<OrgTreeDto> childMenus = loadOrgTree(org);
                org.setChildren(childMenus);
            }
        }
        //将当前的子菜单添加到父菜单中
        tree.setChildren(orgTreeDtos);
        return orgTreeDtos;
    }

    /**
     * @Author: lin.sj
     * @Date: 2021/7/13 22:56
     * @Desc: 获得当前节点到根节点的菜单Id集合,返回包括入参Id的集合
     **/
    private String getOrgPids(Long orgPid){
        SysOrg sysOrg = sysOrgMapper.selectById(orgPid);
        List<Long> pids = getOrgPidsArray(sysOrg.getOrgPids());
        pids.add(orgPid);
        return pids.toString();
    }

    /**
     * @Author: lin.sj
     * @Date: 2021/7/13 22:53
     * @Desc: 将orgPids转换成集合返回，orgPids结构为[1,2,3,4...](公用方法)
     **/
    private List<Long> getOrgPidsArray(String orgPids){
        String[] pidsArray = orgPids.replace("[", "").replace("]", "").replace(" ","").split(",");
        List<String> pidsStrList = new ArrayList<>(Arrays.asList(pidsArray));
        List<Long> pids = pidsStrList.stream().map(pid -> Convert.toLong(pid)).collect(Collectors.toList());
        return pids;
    }

    /**
     * @Author: lin.sj
     * @Date: 2021/7/13 23:00
     * @Desc: 更新同级树节点下的排序，按以下规则：
     * 1.当前节点newSort>oldSort，将大于oldSort 和 小于等于newSort的同级节点排序-1
     * 2.当前节点newSort<oldSort，将大于等于newSort 和 小于oldSort的同级节点排序+1
     **/
    private void updateTreeSort(Integer oldSort,Integer newSort,Integer level,Long pid){
        LambdaUpdateWrapper w = new LambdaUpdateWrapper();
        if(newSort.intValue() == oldSort.intValue()){
            return;
        }else if(newSort.intValue() > oldSort.intValue()){
            w.setSql("sort = sort-1 where sort>"+oldSort+" and sort<="+newSort+" and level = "+level+" and org_pid="+pid);
        }else if(newSort.intValue() < oldSort.intValue()){
            w.setSql("sort = sort+1 where sort>="+newSort+" and sort<"+oldSort+" and level = "+level+" and org_pid="+pid);
        }
        sysOrgMapper.update(null,w);
    }
}
